Note on tempStringOf vs stringOf

I have noticed that in a case tempStringOf( buffer ) does not return the same string as stringOf( buffer ).  It appears that the stringOf() function respects the length field in a buffer structure where as the tempStringOf() function does not.  A buffer object is a pointer to a structure with several "fields". A few of them have obvious meaning. The decoded fields I found are as follows:

offset 0 length 4

Bytes allocated to the buffer string.

offset 8 length 4

Pointer to the memory region containing the string.

offset 12 length 4

The valid length of the string in buffer.

I was using the following code to take the last three characters off the end of my buffer's string:

length( b1, length( b1 ) - 3 )

My code was working when I used stringOf(b1) after the shortening. I then switched to using tempStringOf(b1) instead and then found the last three characters were still a part of the string.

The buffer length() function shortened the buffer by changing the buffer's length in the buffer structure but did not change the location of the string's null terminator in the string. The tempStringOf() function appears to just return a pointer to the buffer's string. Where as the stringOf() function is making a copy of the buffer's string and littering the string table with it. So I suppose the tempStringOf() function is doing what is desired. However the buffer length() function is being "lazy" and not providing a result that works with the tempStringOf() function.

Below is some code to demonstrate this:

{code}

void printHexNibble( int nibble )
{
   print charOf( nibble + (nibble < 10 ? intOf('0') : intOf('A') - 10 ) );
}

void printHexByte( int byte )
{
   printHexNibble( ( byte & 0xF0 ) / 16 );
   printHexNibble( byte & 0x0F );
}

void printHexWord( int word )
{
      printHexByte( (word / 0x1000000 ) & 0xFF );
      printHexByte( (word / 0x0010000 ) & 0xFF );
      printHexByte( (word / 0x0000100 ) & 0xFF );
      printHexByte( (word             ) & 0xFF );
}

int peek32( int * address, int offset )
{
   address += offset;
   return (*address) | 0;
}

int peek8( int * address, int offset )
{
   address += offset;
   return *address & 255;
}

void printMemHexWord( int * address, int size )
{
   int i;

   for( i = 0; i < size; i++ )
   {
      printHexWord( peek32( address, i*4 ) );
      print "\n";
   }
}

void printMemCharByte( int * address, int size )
{
   int i;

   for( i = 0; i < size; i++ )
   {
      print charOf( peek8( address, i ) );
   }
}

// create buffer with an allocation of 16 bytes for the string

Buffer b1 = create( 16 );

b1 = "0123456789";

// get pointer to the buffer's structure

int *ptr = addr_( b1 );

// get pointer to the strings allocation
int *strPtr = addr_( peek32( ptr, 8  ) );

// get number of bytes allocated to the string
int alloc   = addr_( peek32( ptr, 0  ) );

// get length of the string
int len     = addr_( peek32( ptr, 12 ) );

print "Buffer b1 is at address: ";
printHexWord( (int addr_( ptr ) ) );
print "\n";

print "strPtr: ";
printHexWord( (int addr_( strPtr ) ) );
print "\n";

print "alloc: " alloc "\n";
print "len: " len "\n";

print "string contents:\n";
printMemCharByte( strPtr, len );
print "\n\n"

print "allocation contents:\n"
printMemHexWord( strPtr, (alloc+3)/4 )
print "\n\n";

print "length(b1): " (length( b1 )) "\n";

print "shorten b1 by 3 characters\n";

length( b1, length( b1 ) - 3 );

print "length(b1): " (length( b1 )) "\n";

print "\n";
print "-->" stringOf( b1 ) "<-- stringOf(b1)\n";
print "-->" tempStringOf( b1 ) "<-- tempStringOf(b1)\n";
print "\n";

int *strPtr2 = addr_( peek32( ptr, 8  ) );
alloc = (int addr_( peek32( ptr, 0  ) ) );
len   = (int addr_( peek32( ptr, 12 ) ) );

print "Buffer b1 is at address: ";
printHexWord( (int addr_( ptr ) ) );
print "\n";

print "strPtr: ";
printHexWord( (int addr_( strPtr2 ) ) );
print "\n";

print "alloc: " alloc "\n";
print "len: " len "\n";

print "string contents:\n";
printMemCharByte( strPtr2, len );
print "\n\n";

print "allocation contents:\n";
printMemHexWord( strPtr2, (alloc+3)/4 );
print "\n\n";
 

{/code}


GothSloth - Tue Aug 13 13:35:38 EDT 2013

Re: Note on tempStringOf vs stringOf
llandale - Tue Aug 13 15:56:32 EDT 2013

Yes I've noticed that as well.  There was another problem when the Buffer naturally had a null character in it (such as when reading an esecutable file into the Buffer) but I don't recall exactly the issue.

Anyway, don't use "tempStringOf" when you have adjusted it's length.

Re: Note on tempStringOf vs stringOf
Adamarla - Wed Aug 14 03:26:24 EDT 2013

Just set the null terminator as well as the length:

 

Buffer aBuffer = create()
aBuffer = "0123456789"
 
int iLength = length(aBuffer)-3
set(aBuffer, iLength, '\0')
length(aBuffer, iLength)
 
print("[" (stringOf(aBuffer)) "]\n")
print("[" (tempStringOf(aBuffer)) "]\n")
 
delete(aBuffer)

-Adam

Re: Note on tempStringOf vs stringOf
GothSloth - Wed Aug 14 08:48:58 EDT 2013

Adamarla - Wed Aug 14 03:26:24 EDT 2013

Just set the null terminator as well as the length:

 

Buffer aBuffer = create()
aBuffer = "0123456789"
 
int iLength = length(aBuffer)-3
set(aBuffer, iLength, '\0')
length(aBuffer, iLength)
 
print("[" (stringOf(aBuffer)) "]\n")
print("[" (tempStringOf(aBuffer)) "]\n")
 
delete(aBuffer)

-Adam

Thanks Adam excellent solution.

I had tried using a sub-string extraction only to discover it creates and returns a new buffer instead of writing into my buffer. Ugg ... that's when I decided it was time to go sleep on it ;)

void func( Buffer input, Buffer output )
{   Buffer work = create
   ...
   output = work[0:length(work)-3]  //<--- This created a new buffer and assigned it to output.
                                    //     At this point output is no longer pointing to the buffer
                                    //     that was passed into the function and all work done to
                                    //     output after this point was done to a buffer that is not
                                    //     visible to the calling scope.
}     
              

Re: Note on tempStringOf vs stringOf
Adamarla - Wed Aug 14 09:12:08 EDT 2013

GothSloth - Wed Aug 14 08:48:58 EDT 2013

Thanks Adam excellent solution.

I had tried using a sub-string extraction only to discover it creates and returns a new buffer instead of writing into my buffer. Ugg ... that's when I decided it was time to go sleep on it ;)

void func( Buffer input, Buffer output )
{   Buffer work = create
   ...
   output = work[0:length(work)-3]  //<--- This created a new buffer and assigned it to output.
                                    //     At this point output is no longer pointing to the buffer
                                    //     that was passed into the function and all work done to
                                    //     output after this point was done to a buffer that is not
                                    //     visible to the calling scope.
}     
              

Yes, by extracting part of a Buffer you create a new Buffer with the extracted characters.

This is a common way to leak Buffer objects. You always need to assign the result to a Buffer variable so it can be deleted.

Buffer aBuffer = create()

aBuffer = "0123456789"

Buffer subBuffer = aBuffer[2:6]

print(tempStringOf(subBuffer))

delete(subBuffer)

delete(aBuffer)

-Adam

Re: Note on tempStringOf vs stringOf
llandale - Wed Aug 14 17:14:41 EDT 2013

GothSloth - Wed Aug 14 08:48:58 EDT 2013

Thanks Adam excellent solution.

I had tried using a sub-string extraction only to discover it creates and returns a new buffer instead of writing into my buffer. Ugg ... that's when I decided it was time to go sleep on it ;)

void func( Buffer input, Buffer output )
{   Buffer work = create
   ...
   output = work[0:length(work)-3]  //<--- This created a new buffer and assigned it to output.
                                    //     At this point output is no longer pointing to the buffer
                                    //     that was passed into the function and all work done to
                                    //     output after this point was done to a buffer that is not
                                    //     visible to the calling scope.
}     
              

First off, if you figure to allocate or de-allocate a Buffer in a call parameter you'd want to call-by reference:  Modifying contents doesn't require that.

  • void func( Buffer input, Buffer &output )

You could cast the buffer substring to make sure you get a string

  • output  = create()
  • output = (string work[0:length(work)-3])

You can use combine

  • output = create()
  • combine(output, work, 0, length(work)-3)

-Louie

Re: Note on tempStringOf vs stringOf
LucLeto - Fri Aug 21 04:58:33 EDT 2015

Adamarla - Wed Aug 14 09:12:08 EDT 2013

Yes, by extracting part of a Buffer you create a new Buffer with the extracted characters.

This is a common way to leak Buffer objects. You always need to assign the result to a Buffer variable so it can be deleted.

Buffer aBuffer = create()

aBuffer = "0123456789"

Buffer subBuffer = aBuffer[2:6]

print(tempStringOf(subBuffer))

delete(subBuffer)

delete(aBuffer)

-Adam

Does this behaviour also apply to this code:

char chrTest = aBuffer[1] //Does this line also cause a memory leak?
if(chrTest == '1'){
    print("cool")
}

I would guess not because I assigned it to a char type variable. But this is DXL so you can never know.

Thank you in advance for your help :)

 

Edit: Fixed typo

Re: Note on tempStringOf vs stringOf
Mathias Mamsch - Fri Aug 21 05:10:54 EDT 2015

LucLeto - Fri Aug 21 04:58:33 EDT 2015

Does this behaviour also apply to this code:

char chrTest = aBuffer[1] //Does this line also cause a memory leak?
if(chrTest == '1'){
    print("cool")
}

I would guess not because I assigned it to a char type variable. But this is DXL so you can never know.

Thank you in advance for your help :)

 

Edit: Fixed typo

Chars don't leak memory, they exist only on the stack.

string sTest = aBuffer[1:1]    will also not leak! Since no temp Buffer will be created, the string ::[] ( Buffer, Range_) operator is called.

string sTest = aBuffer[1:1] ""  will leak. Temp Buffer is created, since  Buffer ::[] (Buffer, Range_) operator is called and concatenated to a string.

Regards, Mathias

Re: Note on tempStringOf vs stringOf
LucLeto - Fri Aug 21 05:20:56 EDT 2015

Mathias Mamsch - Fri Aug 21 05:10:54 EDT 2015

Chars don't leak memory, they exist only on the stack.

string sTest = aBuffer[1:1]    will also not leak! Since no temp Buffer will be created, the string ::[] ( Buffer, Range_) operator is called.

string sTest = aBuffer[1:1] ""  will leak. Temp Buffer is created, since  Buffer ::[] (Buffer, Range_) operator is called and concatenated to a string.

Regards, Mathias

Wow that was quick. Thank you :)

But what happens if I do this:

if(aBuffer[1] == '1'){
    //then do something
}

This won't leak either, because I derive of your posts that only string range expressions of buffers have this strange behaviour.

 

Offtopic: To Mathias, Louie and all the other DXL gents. Have you ever considered putting all that off manuel knowlegde in a book? I would definitely buy that.

I struggle every day at getting the best out of our scripts. Even 60% of the time I reimplement old libraries to adept to current needs.